package com.szewec.ps.util;

public class LocationUtils {
    static double EARTH_RADIUS = 6371.0;//km 地球半径 平均值，千米
    /**
     * SZ转WGS84
     * Tip: 本方法代码只在勘察BIM项目中用，其中参数数据保密，不得以任何方式外泄
     *
     * @param x X坐标
     * @param y Y坐标
     * @return  经纬度
     */
    public static double[] coordinateToLonLat(double x, double y) {
        double[] rst = new double[2];
        double v4;
        double v5;
        double v6;
        double v7;
        double v8;
        double v9;
        double v10;
        double v12;

        double a3 = (0.9998630461 * x - y * 0.01708078513 + 391100.2004852822) / 1.000017864177028;
        double a4 = (y * 0.9998630461 + x * 0.01708078513 + 2472706.440970496) / 1.000017864177028;

        v12 = a4 * 0.000000157048761142065;
        v4 = Math.sin(v12);
        v5 = Math.sin(v12 + v12)
                * (v4 * v4 * ((0.0000001207696828 - v4 * (0.0000000010757667 * v4)) * (v4 * v4) - 0.0000149236215362)
                + 0.0025262508558867)
                + v12;
        v6 = Math.tan(v5);
        v7 = Math.cos(v5);
        v8 = v7 * (0.00673949674227 * v7);
        v9 = v8 + 1.0;
        v10 = (a3 - 500000.0) / (6399593.6258 / Math.sqrt(v9));
        rst[0] = (((v8 * 2.0 * (4.0 * v6 * v6 + 3.0)
                + 4.0 * v6 * v6 * (v6 * 6.0 * v6 + 7.0)
                + 5.0
                - (((v6 * 720.0 * v6 + 1320.0) * v6 * v6 + 662.0) * v6 * v6 + 61.0) * v10 * v10 / 42.0) * v10 * v10 / 20.0
                - (v6 * 2.0 * v6
                + 1.0
                + v8)) * v10 * v10 / 6.0 + 1.0) * (v10 * 57.29577951308232 / v7)
                + 114.0;
        rst[1] = v10 * (((((((v6 * 1575.0 * v6 + 4095.0) * v6 * v6 + 3633.0) * v6 * v6 + 1385.0) * v10 * v10 / 56.0
                - (((-162.0 - v6 * 45.0 * v6) * v6 * v6 + 107.0) * v8
                + v6 * 45.0 * v6 * (v6 * v6 + 2.0)
                + 61.0)) * v10 * v10 / 30.0
                + ((-2.0 - v8 * 3.0) * v8 + 1.0) * (v6 * 3.0 * v6)
                + 5.0
                + (2.0 - v8) * (v8 * 3.0)) * v10 * v10 / 12.0
                - v9) * (v6 * 57.29577951308232) * v10) * 0.5
                + v5 / 0.01745329251994328
                - 0.000045;
        return rst;
    }

    private static final double pi = 3.1415926535897932384626;
    private static final double a = 6378245.0;
    private static final double ee = 0.00669342162296594323;
    private static final double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    /**
     * 经纬度转国测局
     * @param lon 经度
     * @param lat 纬度
     * @return 国测局坐标
     */
    public static double[] LLToGCJ02(double lon, double lat) {
        double[] rst = new double[2];
        double tlat = transformLat(lon - 105.0, lat - 35.0);
        double tlon = transformLon(lon - 105.0, lat - 35.0);
        double rlat = lat / 180.0 * pi;
        double magic = Math.sin(rlat);
        magic = 1 - ee * magic * magic;
        double sqrtM = Math.sqrt(magic);
        tlat = (tlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtM) * pi);
        tlon = (tlon * 180.0) / (a / sqrtM * Math.cos(rlat) * pi);

        rst[0] = lon + tlon;
        rst[1] = lat + tlat;
        return rst;
    }

    /**
     *  国测局转百度09
     * @param lon 经度
     * @param lat 纬度
     * @return 百度09
     */
    public static double[] GCJ02ToBD09(double lon, double lat) {
        double[] rst = new double[2];
        double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
        double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * x_pi);
        double bdLon = z * Math.cos(theta) + 0.0065;
        double bdLat = z * Math.sin(theta) + 0.006;
        rst[0] = bdLon;
        rst[1] = bdLat;
        return rst;
    }

    private static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y
                + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1
                * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0
                * pi)) * 2.0 / 3.0;
        return ret;
    }

    /**
     * 根据两个点的经纬度计算两点的距离
     * @param lon1 坐标1的经度
     * @param lat1 坐标1的纬度
     * @param lon2 坐标2的经度
     * @param lat2 坐标2的纬度
     * @return 两点的距离(米)
     */
    public static double getDistance(double lon1,double lat1, double lon2, double lat2)
    {
        //用haversine公式计算球面两点间的距离。
        //经纬度转换成弧度
        lon1 = ConvertDegreesToRadians(lon1);
        lat1 = ConvertDegreesToRadians(lat1);
        lon2 = ConvertDegreesToRadians(lon2);
        lat2 = ConvertDegreesToRadians(lat2);

        //差值
        double vLon = Math.abs(lat1 - lat2);
        double vLat = Math.abs(lon1 - lon2);
        //h is the great circle distance in radians, great circle就是一个球体上的切面，它的圆心即是球心的一个周长最大的圆。
        double h = HaverSin(vLat) + Math.cos(lon1) * Math.cos(lon2) * HaverSin(vLon);

        return 2 * EARTH_RADIUS * Math.asin(Math.sqrt(h)) * 1000;
    }

    private static double HaverSin(double theta)
    {
        double v = Math.sin(theta / 2);
        return v * v;
    }

    private static double ConvertDegreesToRadians(double degrees)
    {
        return degrees * Math.PI / 180;
    }


    /**
     * 解析从ExifInterface获取到的照片的经纬度信息
     * @param rationalString 经度字符串(纬度字符串)
     * @param ref N北纬S南纬E东经W西经
     * @return double型的经纬度
     */
    public static double convertRationalLatLonToDouble(String rationalString, String ref) {
        try {
            String [] parts = rationalString.split(",");

            String [] pair;
            pair = parts[0].split("/");
            double degrees = Double.parseDouble(pair[0].trim())
                    / Double.parseDouble(pair[1].trim());

            pair = parts[1].split("/");
            double minutes = Double.parseDouble(pair[0].trim())
                    / Double.parseDouble(pair[1].trim());

            pair = parts[2].split("/");
            double seconds = Double.parseDouble(pair[0].trim())
                    / Double.parseDouble(pair[1].trim());

            double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
            if ((ref.equals("S") || ref.equals("W"))) {
                return -result;
            }
            return result;
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException();
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new IllegalArgumentException();
        }
    }
}
